package com.yeeverse.api.demo.utils;

import org.apache.commons.io.IOUtils;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 *
 */

/**
 * <p>
 * RSA公钥/私钥/签名工具包
 * </p>
 * <p>
 * 罗纳德·李维斯特（Ron [R]ivest）、阿迪·萨莫尔（Adi [S]hamir）和伦纳德·阿德曼（Leonard [A]dleman）
 * </p>
 * <p>
 * 字符串格式的密钥在未在特殊说明情况下都为BASE64编码格式<br/>
 * 由于非对称加密速度极其缓慢，一般文件不使用它来加密而是使用对称加密，<br/>
 * 非对称加密算法可以用来对对称加密的密钥加密，这样保证密钥的安全也就保证了数据的安全
 * </p>
 *
 * @author IceWee
 * @date 2012-4-26
 * @version 1.0
 */
public class RSAUtils {

    public static final String RSA_ALGORITHM = "RSA"; // ALGORITHM ['ælgərɪð(ə)m] 算法的意思


    /** */
    /**
     * 签名算法
     */
    public static final String SIGNATURE_ALGORITHM = "SHA256withRSA";


    /** */
    private static String pk = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC/lnKHfQUIDVUdZwhg+KB9wQj9qP/elQT729ljFb0jNn6RDP0MQCAq/0rN3rRFsqOJiUvDCkqkmU4MZv2m5eCx1BdlhsX6CI4rB5m3bYYwl/60KxV4zFhLLa6E24zhhwz//U7WNnzpSQJNw+47MPbyafugJevsblIVvp/Ql1QD+03Z3l9BtenRGhkn3+lY1sDX4O1wyUOlhSBjttMxqedF/hFOgCcPcwSNvUbBg6Ooz2Y68rcT0m8sI2+o7Mc1KjZrNwVExBwAvQPGVRvTcqYZqzsOhwCQzNCoemKtDaDhxTUwF2Y/Z4GnvJcIpMV6xBGvu7xfedNVOMbb60MnHD9LAgMBAAECggEAAz9yq5dT+YH4Wmo/ux0b42XdWCeARu6xye89LS/WJ/2pgZ39wDJqGTCAvSK0XaYcnZ1FOoY3I7kxmIY6cJTfDUSyLNdvDcENX+9eDLz/lF1PRRCh6Y1VRdj9Z2UtazlV4BUUPA9qaK4DKvxSUpCO7ycay+k4qaZpH9M+dFPMadDWQ3UqfHOThw1gREsFEm0sRpARQufHAZr3S/V1ai8L3n7DnRDeBOrZXoaOmfV9S64DW9dahCyHDg/ZwRxnBN0rp0Qx+TeEP8wqk9YryNyhFjciVmIdI95jga49dlzqZz5enXdR6tv/R4KtTw2xqMpdYWyvjMSiYWgrpbaeFIow0QKBgQDzDtbOcoU9xDHqRCSiwVpioCEa7rffD5vqPEK/hyvmSVDviC3+c00JPOMNCJ2i4XhPAdTHqwaclYfYxkNzkcMGxgwJDhfQ7PNPpj482+eAB8UNyzaPVZ+EWDQ5/7RcV97m5m876055LpNOunB/3RLTdT0/DjRKfEhp8zF+OINKwwKBgQDJygJHZH1oZEJve/mi43gPA1095Dgf1NKnFCd9gzbKe35UvMNG2FGyL3VFWt1rMfR19eZSI6i+3dSL1X2/LznnbA7RfYfHbvCPBsz+EQZv11IeKo4TOPs1t1TffnxYjHyS/dzYBrIAhZ06FNQFcl4FXM/VsgHBhaEVvDFoCQig2QKBgQCesxawT3gOH1dUFdAx8Lt6YAGdmza1T3THGTef5LtWLpLJA4q8dD5dhMLfVLvQiRVLyO9Mu6+id8PuV23dPNQxTQi4tNn6EWzAmdRTWmYqY2hXnFLwfcCc4haftATosnsZGmaQohBn8XA/XF5uLkPHi+pEjJq587I4c5DiUDXxjwKBgQCEY2FtX3ObqrOfmvHkOWLKTsW81l8wGxv9fA6QnSa0X6M5iwJ/F2s6AULqNrwwGP0u6x6p4zVRzA4nk/DFHwHZYB60uKv9Xz66R5b0VU0o0VbR7c96sxa1XktzufNw0PLmKWHb5UChcQf/VCzqSed/ky3UIM5gHgDaVrb/b3XWuQKBgQCGjGJ1QDv/bxWlgoFl+h2/O6UjzaTK0eGWpuM3DNhK1or+T5wJOLkUlyDHwMgJaXvcHf4cMQL2oT4TkSekBwa+E1qBpgx54hJvsmlRW6QP8eDd8l6vJnvOtngm3uDCWHEMSgpNJMD1Zlt2Urd4RMjGzM0NLKS3vKRA9abAcBuKDQ==";

    /** */
    private static String pv = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv5Zyh30FCA1VHWcIYPigfcEI/aj/3pUE+9vZYxW9IzZ+kQz9DEAgKv9Kzd60RbKjiYlLwwpKpJlODGb9puXgsdQXZYbF+giOKweZt22GMJf+tCsVeMxYSy2uhNuM4YcM//1O1jZ86UkCTcPuOzD28mn7oCXr7G5SFb6f0JdUA/tN2d5fQbXp0RoZJ9/pWNbA1+DtcMlDpYUgY7bTMannRf4RToAnD3MEjb1GwYOjqM9mOvK3E9JvLCNvqOzHNSo2azcFRMQcAL0DxlUb03KmGas7DocAkMzQqHpirQ2g4cU1MBdmP2eBp7yXCKTFesQRr7u8X3nTVTjG2+tDJxw/SwIDAQAB";

    /**
     * <p>
     * 用私钥对信息生成数字签名
     * </p>
     *
     * @param data 已加密数据
     * @param privateKey 私钥(BASE64编码)
     *
     * @return
     * @throws Exception
     */
    public static String sign(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateK);
        signature.update(data);
        return Base64.getEncoder().encodeToString(signature.sign());
    }

    /**
     * <p>
     * 校验数字签名
     * </p>
     *
     * @param data 已加密数据
     * @param publicKey 公钥(BASE64编码)
     * @param sign 数字签名
     *
     * @return
     * @throws Exception
     *
     */
    public static boolean verify(byte[] data, String publicKey, String sign)
            throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        PublicKey publicK = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicK);
        signature.update(data);
        return signature.verify(Base64.getDecoder().decode(sign));
    }

    /**
     * 私钥解密
     * @param data
     * @param privateKey
     * @return
     */

    public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.getDecoder().decode(data), privateKey.getModulus().bitLength()), "utf-8");
        } catch (Exception e) {
            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
        }
    }

    /**
     * 得到公钥
     * @param publicKey  密钥字符串（经过base64编码）
     * @throws Exception
     */
    public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        // 通过X509编码的Key指令获得公钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
        RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        return key;
    }

    /**
     * 得到私钥
     * @param privateKey  密钥字符串（经过base64编码）
     * @throws Exception
     */
    public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        // 通过PKCS#8编码的Key指令获得私钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
        RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
        return key;
    }

    //rsa切割解码  , ENCRYPT_MODE,加密数据   ,DECRYPT_MODE,解密数据
    private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) {
        int maxBlock = 0;  //最大块
        if (opmode == Cipher.DECRYPT_MODE) {
            maxBlock = keySize / 8;
        } else {
            maxBlock = keySize / 8 - 11;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] buff;
        int i = 0;
        try {
            while (datas.length > offSet) {
                if (datas.length - offSet > maxBlock) {
                    //可以调用以下的doFinal（）方法完成加密或解密数据：
                    buff = cipher.doFinal(datas, offSet, maxBlock);
                } else {
                    buff = cipher.doFinal(datas, offSet, datas.length - offSet);
                }
                out.write(buff, 0, buff.length);
                i++;
                offSet = i * maxBlock;
            }
        } catch (Exception e) {
            throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);
        }
        byte[] resultDatas = out.toByteArray();
        IOUtils.closeQuietly(out);
        return resultDatas;
    }

    /**
     * 对外包装使用公钥加密算法
     * @param data
     * @param publicKey
     * @return
     * @throws Exception
     */
    public static String encryptByPublicKey(String data, String publicKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            RSAPublicKey publicKey1 = getPublicKey(publicKey);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey1);
            return Base64.getEncoder().encodeToString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(), publicKey1.getModulus().bitLength()));
        } catch (Exception e) {
            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
        }
    }

    /**
     * 对外包装使用私钥解密算法
     * @param data
     * @return
     * @throws Exception
     */
    public static String decryptByPrivateKey(String data, String privateKey) throws Exception {
        return decryptByPrivateKey(data, getPrivateKey(privateKey));
    }

    public static void main(String[] args) throws Exception {
        String data = "{\"agentNo\":\"1020000100000602\",\"bizAmount\":\"0.04\",\"merchantNo\":\"1020000000200007\",\"requestNo\":\"test1599206886080\",\"requestTime\":\"2020-09-04 16:08:06\"}";

//        String aesKey="dsdfsdgxcvxcv";


        String s1 = encryptByPublicKey(data, pv);
        System.out.println(s1);


//        String s2=sign(data.getBytes(),pk);
//        System.out.println(s2);


//        String s = decryptByPrivateKey("1Ywm1RiWQV53dIG1PDCmTW6z9XPIsPGzqRjAX+ZZ6ensS4U/jWXdjRRqEGyfqkuhrKk88WRzQHtBzDD0Ce32HBNzc/qklYasezN/5Iej/iIW79uDRtHkfOtzAgV8tpx9SWsdqsOrmfDa5WkgP6ZjGt5ax75LLhkI0v/fye1qmdg6gGZsx6tAZLBUzpiIEhbEem6iD/3vRhv4jg8MM44fpiR6ZSV1rXM74n6yOFwVbldFWOPZnoWHjatYj/mwbI8dWm+Lw51uU+5r7LPkMI+DvAMZ7De3CVkcHD2WMX1mkC/xeZwinuyc78TnrQNiQhVx9X/dpT3dwGx6HQCc/Kv4hvA==", pk);
//        System.out.println(s);

//        boolean verify = verify(data.getBytes(), pv, "S4VsmFtC3zcYOSaANUQ0tATEyUp6xP7k59oOpKQ5nWja2ODpGfYwXl7KYQ+BIuO1/3kb5PzSAuKhxZbtHqaVSxxip4WoHlQZg0Vwu6P2ly/cHa44gbBJQRe5X3/YGo/y/ciPC9ylX1p6drxD1V6nf4R3tT7H9oyOx8qmdV0Fxubhniekt9KIj0CxgH6DEX+y+yCrXhQkngbbFshbPsKG7DCfFUR5BkZD00xMXn/sXj4EYBazgQM0p6X2VBKkwMO1Jorp9RhVx+rFhRSswSliFJpEJals5t1XILcE38dbBDWUfyvpYHldloR7F6ovpoGuhn06Hl2GSfC78GMBhs8vVQ==");
//        System.out.println(verify);


    }

}
